// -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/build/dump -XX:+PrintGCDetails  -XX:+PrintTenuringDistribution -XX:+PrintGCDateStamps -Xloggc:/home/build/dump/heap_trace.txt

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// 截取后数据存放对象
type OrdinaryTXT struct {
	time      string
	gctype    string
	writebyte string
	consuming string
}

type FullTXT struct {
	time      string
	propdata  string
	consuming string
}

// 普通gc间隔
type OrdinaryPI struct {
	maxpi   int // 毫秒
	minpi   int
	avgpi   int
	sumpi   int
	oldunix int64
}

// 普通gc时长
type OrdinaryET struct {
	// gctype  string
	maxcpu  int // 毫秒（放大了100倍）
	mincpu  int
	avgcpu  int
	sumcpu  int
	maxsys  int
	minsys  int
	avgsys  int
	sumsys  int
	maxrel  int
	minrel  int
	avgrel  int
	sumrel  int
	relprop int // rel>cpu+sys的次数
}

// 写入老年代大小
type OrdinaryWK struct {
	maxwk int // kb
	minwk int
	avgwk int
	sumwk int
}

type OrdinaryAllPI struct {
	AllocationF *OrdinaryPI
	GCLockerIG  *OrdinaryPI
}

type OrdinaryAllET struct {
	AllocationF           *OrdinaryET
	GCLockerIG            *OrdinaryET
	AllocationF_frequency int
	GCLockerIG_frequency  int
}

type OrdinaryGc struct {
	oapi          *OrdinaryAllPI
	oaet          *OrdinaryAllET
	owk           *OrdinaryWK
	exe_frequency int // 执行频率
}

// full gc
type FulloldProp struct {
	maxprop int
	minprop int
	avgprop int
	sumprop int
}

type FullGc struct {
	fop           *FulloldProp
	fet           *OrdinaryET
	fpi           *OrdinaryPI
	exe_frequency int
}

var (
	timeTemplate string = "2006-01-02T15:04:05.000"
	//正则
	// reg1 *regexp.Regexp = regexp.MustCompile(`([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3})+0800:\s[0-9]{5}\.[0-9]{3}:\s\[GC(.*)`)
	OrdinaryGcreg = regexp.MustCompile(`[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3}|GC\s\(.*?\)|\[PSYoungGen:.*?secs\]|\[Times:.*secs\]`)
	shuntreg      = regexp.MustCompile(`Full\sGC`)
	findGcTime    = regexp.MustCompile(`^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3}`)
	findGcType    = regexp.MustCompile(`\[GC\s\(.*?\)`)
	findGcK       = regexp.MustCompile(`\[PSYoungGen:.*?secs\]`)
	findGcT       = regexp.MustCompile(`\[Times:.*?secs\]`)
	extractGcType = regexp.MustCompile(`\[GC\s\((.*?)\)`)
	extractGcK    = regexp.MustCompile(`\[PSYoungGen:\s(.*?)K->(.*?)K\([0-9]+K\)\]\s(.*?)K->(.*?)K\([0-9]+K\).*`)
	extractGcT    = regexp.MustCompile(`\[Times:\suser=(.*?)\ssys=(.*?),\sreal=(.*?)\s.*`)
	//full
	findGcold     = regexp.MustCompile(`\[ParOldGen:.*?\]`)
	extractFGcOld = regexp.MustCompile(`\[ParOldGen:\s[0-9]+K->(.*?)K\((.*?)K\)\]`)
	zero          = 0
	//对象
	ogc = &OrdinaryGc{}
	fgc = &FullGc{}
	// a =
)

func init() {
	ogc.InitData()
	fgc.initData()
}

func (opi *OrdinaryPI) swapMax(data int) {
	if opi.maxpi < data {
		opi.maxpi = data
	}
}

func (opi *OrdinaryPI) swapMin(data int) {
	if opi.minpi > data || opi.minpi == zero {
		opi.minpi = data
	}
}

func (opi *OrdinaryPI) genAvg(num int) {
	opi.avgpi = opi.sumpi / (num - 1)
}

func (api *OrdinaryAllPI) recordData(gctype string, dateunix int64) {
	switch gctype {
	case "GCLocker Initiated GC":
		if api.GCLockerIG.oldunix == 0 {
			api.GCLockerIG.oldunix = dateunix
			return
		}
		datepoor := int(dateunix - api.GCLockerIG.oldunix)
		api.GCLockerIG.swapMax(datepoor)
		api.GCLockerIG.swapMin(datepoor)
		api.GCLockerIG.sumpi += datepoor
		api.GCLockerIG.oldunix = dateunix
	case "Allocation Failure":
		if api.AllocationF.oldunix == 0 {
			api.AllocationF.oldunix = dateunix
			return
		}
		datepoor := int(dateunix - api.AllocationF.oldunix)
		api.AllocationF.swapMax(datepoor)
		api.AllocationF.swapMin(datepoor)
		api.AllocationF.sumpi += datepoor
		api.AllocationF.oldunix = dateunix
	}
}

func (api *OrdinaryAllPI) GenAvg(num int) {
	api.AllocationF.genAvg(num)
	api.GCLockerIG.genAvg(num)
}

func (api *OrdinaryAllPI) initData() {
	api.AllocationF = new(OrdinaryPI)
	api.GCLockerIG = new(OrdinaryPI)
}

func (oet *OrdinaryET) swapMax(datac, datas, datar int) {
	if oet.maxcpu < datac {
		oet.maxcpu = datac
	}

	if oet.maxsys < datas {
		oet.maxsys = datas
	}

	if oet.maxrel < datar {
		oet.maxrel = datar
	}
}

//非零最小值
func (oet *OrdinaryET) swapMin(datac, datas, datar int) {
	if oet.mincpu > datac && datac != zero || oet.mincpu == zero && datac > zero {
		oet.mincpu = datac
	}

	if oet.minsys > datas && datas > zero || oet.minsys == zero && datas > zero {
		oet.minsys = datas
	}

	if oet.minrel > datar && datar > zero || oet.minrel == zero && datar > zero {
		oet.minrel = datar
	}
}

func (oet *OrdinaryET) genAvg(num int) {
	oet.avgcpu = oet.sumcpu / num
	oet.avgsys = oet.sumsys / num
	oet.avgrel = oet.sumrel / num
}

func (aet *OrdinaryAllET) recordData(gctype string, consuming []int) {

	switch gctype {
	case "GCLocker Initiated GC":
		aet.GCLockerIG.sumcpu += consuming[0]
		aet.GCLockerIG.sumsys += consuming[1]
		aet.GCLockerIG.sumrel += consuming[2]
		aet.GCLockerIG.swapMax(consuming[0], consuming[1], consuming[2])
		aet.GCLockerIG.swapMin(consuming[0], consuming[1], consuming[2])
		if consuming[0]+consuming[1] < consuming[2] {
			aet.GCLockerIG.relprop++
		}
		aet.GCLockerIG_frequency++
	case "Allocation Failure":
		aet.AllocationF.sumcpu += consuming[0]
		aet.AllocationF.sumsys += consuming[1]
		aet.AllocationF.sumrel += consuming[2]
		aet.AllocationF.swapMax(consuming[0], consuming[1], consuming[2])
		aet.AllocationF.swapMin(consuming[0], consuming[1], consuming[2])
		if consuming[0]+consuming[1] < consuming[2] {
			aet.GCLockerIG.relprop++
		}
		aet.AllocationF_frequency++
	}
}

func (aet *OrdinaryAllET) GenAvg(num int) {
	aet.AllocationF.genAvg(num)
	aet.GCLockerIG.genAvg(num)
}

func (aet *OrdinaryAllET) initData() {
	aet.AllocationF = new(OrdinaryET)
	aet.GCLockerIG = new(OrdinaryET)
}

func (wk *OrdinaryWK) swapMax(data int) {
	if wk.maxwk < data {
		wk.maxwk = data
	}
}

func (wk *OrdinaryWK) swapMin(data int) {
	if wk.minwk > data && data > zero || wk.minwk == zero && data > zero {
		wk.minwk = data
	}
}

func (wk *OrdinaryWK) genAvg(num int) {
	wk.avgwk = wk.sumwk / num
}

func (wk *OrdinaryWK) recordData(writebyte int) {
	wk.sumwk += writebyte
	wk.swapMax(writebyte)
	wk.swapMin(writebyte)
}

func (og *OrdinaryGc) InitData() {
	og.oaet = new(OrdinaryAllET)
	og.oapi = new(OrdinaryAllPI)
	og.owk = new(OrdinaryWK)
	og.oaet.initData()
	og.oapi.initData()
}

func (og *OrdinaryGc) Screening(data OrdinaryTXT) {
	og.exe_frequency += 1
	stamp, _ := time.ParseInLocation(timeTemplate, data.time, time.Local)
	dateunix := stamp.UnixNano() / 1e6 //毫秒
	gctype := extractGcType.ReplaceAllString(data.gctype, "$1")
	consumingtxt := extractGcT.ReplaceAllString(data.consuming, "$1|$2|$3")
	consuming := strings.Split(consumingtxt, "|")
	consumingfloat := []int{}
	for _, v := range consuming {
		f, err := strconv.ParseFloat(v, 64)
		i := int(f * 100)
		if err != nil {
			fmt.Println("耗时格式化异常: ", err)
			return
		}
		consumingfloat = append(consumingfloat, i)
	}
	writebytetxt := extractGcK.ReplaceAllString(data.writebyte, "$1|$2|$3|$4")
	writebytedata := strings.Split(writebytetxt, "|")
	writebyteint := []int{}
	for _, v := range writebytedata {
		i, err := strconv.Atoi(v)
		if err != nil {
			fmt.Println("写入数据量格式化异常: ", err)
			return
		}
		writebyteint = append(writebyteint, i)
	}
	writebyte := writebyteint[0] - writebyteint[1] - writebyteint[2] + writebyteint[3]
	og.oapi.recordData(gctype, dateunix)
	og.oaet.recordData(gctype, consumingfloat)
	og.owk.recordData(writebyte)
}

func (og *OrdinaryGc) StructPrint() {
	og.oaet.GenAvg(og.exe_frequency)
	og.oapi.GenAvg(og.exe_frequency - 1)
	og.owk.genAvg(og.exe_frequency)

	fmt.Println("######################普通GC相关信息######################")
	fmt.Printf("普通GC触发次数: %v\n", og.exe_frequency)
	fmt.Printf("进入老年代区大小		最大值:%v		最小值:%v		平均值:%v\n", og.owk.maxwk, og.owk.minwk, og.owk.avgwk)
	fmt.Println("")
	fmt.Printf("Allocation Failure GC 触发次数: %v\n", og.oaet.AllocationF_frequency)
	fmt.Printf("gc操作间隔时间(毫秒)		最大值:%v		最小值:%v		平均值:%v\n", og.oapi.AllocationF.maxpi, og.oapi.AllocationF.minpi, og.oapi.AllocationF.avgpi)
	fmt.Printf("gc执行耗时(CPU 毫秒)		最大值:%v		最小值:%v		平均值:%v\n", intTofloat(og.oaet.AllocationF.maxcpu), intTofloat(og.oaet.AllocationF.mincpu), intTofloat(og.oaet.AllocationF.avgcpu))
	fmt.Printf("gc执行耗时(SYS 毫秒)		最大值:%v		最小值:%v		平均值:%v\n", intTofloat(og.oaet.AllocationF.maxsys), intTofloat(og.oaet.AllocationF.minsys), intTofloat(og.oaet.AllocationF.avgsys))
	fmt.Printf("gc执行耗时(REL 毫秒)		最大值:%v		最小值:%v		平均值:%v\n", intTofloat(og.oaet.AllocationF.maxrel), intTofloat(og.oaet.AllocationF.minrel), intTofloat(og.oaet.AllocationF.avgrel))
	fmt.Printf("gc执行耗时REL>CPU+SYS的次数: %v\n", og.oaet.AllocationF.relprop)
	fmt.Println("")
	fmt.Printf("GCLocker Initiated GC 触发次数: %v\n", og.oaet.GCLockerIG_frequency)
	fmt.Printf("gc操作间隔时间(毫秒)		最大值:%v		最小值:%v		平均值:%v\n", og.oapi.GCLockerIG.maxpi, og.oapi.GCLockerIG.minpi, og.oapi.GCLockerIG.avgpi)
	fmt.Printf("gc执行耗时(CPU 毫秒)		最大值:%v		最小值:%v		平均值:%v\n", intTofloat(og.oaet.GCLockerIG.maxcpu), intTofloat(og.oaet.GCLockerIG.mincpu), intTofloat(og.oaet.GCLockerIG.avgcpu))
	fmt.Printf("gc执行耗时(SYS 毫秒)		最大值:%v		最小值:%v		平均值:%v\n", intTofloat(og.oaet.GCLockerIG.maxsys), intTofloat(og.oaet.GCLockerIG.minsys), intTofloat(og.oaet.GCLockerIG.avgsys))
	fmt.Printf("gc执行耗时(REL 毫秒)		最大值:%v		最小值:%v		平均值:%v\n", intTofloat(og.oaet.GCLockerIG.maxrel), intTofloat(og.oaet.GCLockerIG.minrel), intTofloat(og.oaet.GCLockerIG.avgrel))
	fmt.Printf("gc执行耗时REL>CPU+SYS的次数: %v\n", og.oaet.GCLockerIG.relprop)
}

func (ott OrdinaryTXT) check() bool {
	if ott.consuming != "" && ott.gctype != "" && ott.time != "" && ott.writebyte != "" {
		return true
	} else {
		return false
	}
}

//Full

func (fop *FulloldProp) swapMax(prop int) {
	if fop.maxprop < prop || fop.maxprop == zero {
		fop.maxprop = prop
	}
}

func (fop *FulloldProp) swapMin(prop int) {
	if fop.minprop > prop && prop != zero || fop.minprop == zero && prop != zero {
		fop.minprop = prop
	}
}

func (fop *FulloldProp) genAvg(num int) {
	fop.avgprop = fop.sumprop / num
}

func (fg *FullGc) StructPrint() {
	fg.fop.genAvg(fg.exe_frequency)
	fg.fet.genAvg(fg.exe_frequency)
	fg.fpi.genAvg(fg.exe_frequency - 1)

	fmt.Println("")
	fmt.Println("")
	fmt.Println("######################Full GC相关信息######################")
	fmt.Printf("Full GC触发次数: %v\n", fg.exe_frequency)
	fmt.Printf("Full GC后老年代空闲比例:		最大值:%v		最小值:%v		平均值:%v\n", fg.fop.maxprop, fg.fop.minprop, fg.fop.avgprop)
	fmt.Printf("Full GC操作间隔时间(秒)			最大值:%v		最小值:%v		平均值:%v\n", fg.fpi.maxpi, fg.fpi.minpi, fg.fpi.avgpi)
	fmt.Printf("Full GC执行耗时(CPU 毫秒)		最大值:%v		最小值:%v		平均值:%v\n", intTofloat(fg.fet.maxcpu), intTofloat(fg.fet.mincpu), intTofloat(fg.fet.avgcpu))
	fmt.Printf("Full GC执行耗时(SYS 毫秒)		最大值:%v		最小值:%v		平均值:%v\n", intTofloat(fg.fet.maxsys), intTofloat(fg.fet.minsys), intTofloat(fg.fet.avgsys))
	fmt.Printf("Full GC执行耗时(REL 毫秒)		最大值:%v		最小值:%v		平均值:%v\n", intTofloat(fg.fet.maxrel), intTofloat(fg.fet.minrel), intTofloat(fg.fet.avgrel))
	fmt.Printf("Full GC执行耗时REL>CPU+SYS的次数: %v\n", fg.fet.relprop)
}

func (fg *FullGc) Screening(data FullTXT) {
	fg.exe_frequency += 1
	stamp, _ := time.ParseInLocation(timeTemplate, data.time, time.Local)
	dateunix := stamp.Unix() //秒

	consumingtxt := extractGcT.ReplaceAllString(data.consuming, "$1|$2|$3")
	consuming := strings.Split(consumingtxt, "|")
	consumingint := []int{}
	for _, v := range consuming {
		f, err := strconv.ParseFloat(v, 64)
		i := int(f * 100)
		if err != nil {
			fmt.Println("耗时格式化异常: ", err)
			return
		}
		consumingint = append(consumingint, i)
	}

	oldtxt := extractFGcOld.ReplaceAllString(data.propdata, "$1|$2")
	olddata := strings.Split(oldtxt, "|")
	oldint := []int{}
	for _, v := range olddata {
		i, err := strconv.Atoi(v)
		if err != nil {
			fmt.Println("老年代数据式化异常: ", err)
			return
		}
		oldint = append(oldint, i)
	}
	oldprop := oldint[0] * 100 / oldint[1]

	//
	if consumingint[0]+consumingint[1] < consumingint[2] {
		fg.fet.relprop++
	}
	fg.fet.sumcpu += consumingint[0]
	fg.fet.sumsys += consumingint[1]
	fg.fet.sumrel += consumingint[2]
	fg.fet.swapMax(consumingint[0], consumingint[1], consumingint[2])
	fg.fet.swapMin(consumingint[0], consumingint[1], consumingint[2])

	if fg.fpi.oldunix == 0 {
		fg.fpi.oldunix = dateunix
	} else {
		datepoor := int(dateunix - fg.fpi.oldunix)
		fg.fpi.swapMax(datepoor)
		fg.fpi.swapMin(datepoor)
		fg.fpi.oldunix = dateunix
		fg.fpi.sumpi += datepoor
	}

	fg.fop.swapMax(oldprop)
	fg.fop.swapMin(oldprop)
	fg.fop.sumprop += oldprop
}

func (fg *FullGc) initData() {
	fg.fop = new(FulloldProp)
	fg.fet = new(OrdinaryET)
	fg.fpi = new(OrdinaryPI)
}

func (ftt FullTXT) check() bool {
	if ftt.time != "" && ftt.propdata != "" && ftt.consuming != "" {
		return true
	} else {
		return false
	}
}

func Readfile(path string) {
	datalistO := OrdinaryTXT{}
	datalistF := FullTXT{}

	f, err := os.Open(path)
	if err != nil {
		fmt.Println("error: ", err)
		return
	}

	r := bufio.NewReader(f)
	for {
		d, err := r.ReadBytes('\n')

		if err != nil && err != io.EOF {
			fmt.Println("文件读取异常: ", err)
			return
		}
		datastring := string(d)
		if shuntreg.FindString(datastring) == "" {
			datalistO = dataScreeningO(datastring, datalistO)
		} else {
			datalistF = dataScreeningF(datastring, datalistF)
		}
		if err == io.EOF {
			// fmt.Println("文件读取完成")
			ogc.StructPrint()
			fgc.StructPrint()
			return
		}
	}
}

func dataScreeningO(txt string, datalist OrdinaryTXT) OrdinaryTXT {
	if datalist.time == "" {
		datalist.time = findGcTime.FindString(txt)
	}
	if datalist.gctype == "" {
		datalist.gctype = findGcType.FindString(txt)
	}
	if datalist.writebyte == "" {
		datalist.writebyte = findGcK.FindString(txt)
	}
	if datalist.consuming == "" {
		datalist.consuming = findGcT.FindString(txt)
	}

	if datalist.check() {
		ogc.Screening(datalist)
		datalist = OrdinaryTXT{}
	}
	return datalist
}

func dataScreeningF(txt string, datalist FullTXT) FullTXT {
	if datalist.time == "" {
		datalist.time = findGcTime.FindString(txt)
	}
	if datalist.propdata == "" {
		datalist.propdata = findGcold.FindString(txt)
	}
	if datalist.consuming == "" {
		datalist.consuming = findGcT.FindString(txt)
	}

	if datalist.check() {
		fgc.Screening(datalist)
		datalist = FullTXT{}
	}
	return datalist
}

func intTofloat(n int) float32 {
	v := float32(n)
	return v / 100
}

func main() {
	Readfile(`./heap_trace.txt`)
}